//-------------------------------------------------------------------
//	Copyright (c) 2010-2012 Zhirnov Andrey
//	This file is part of the "UXGEN" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------
/*
*/

#ifndef _UX_MEGA_TEXTURE_H
#define _UX_MEGA_TEXTURE_H

#include "../Textures/CVolumeTexture.h"
#include "../Shaders/CShaderProgram.h"

namespace Engine
{
namespace Resources
{

	//
	// Mega Texture Updater interface
	//

	class IMegaTextureUpdater
	{
	public:
		virtual void operator () (const irect &sNewCoords, const array<i8_vec2> &aBlocks, uint uLayer) = 0;
	};



	//
	// Mega Texture Descriptor
	//

	struct TMTDescriptor
	{
		// types
		struct TTexture
		{
			uint					uTexSize;
			e_rt_format::type		eFormat;
			e_tex_flags::type		eFlags;
			e_tex_swizzle::type		eSwizzle;
			e_megatex_layer::type	eType;
			///
			TTexture()
			{
				memset( this, 0, sizeof(*this) );
			}

			TTexture(uint size, e_rt_format::type format, e_tex_flags::type flags, e_tex_swizzle::type swizzle, e_megatex_layer::type layer):
				uTexSize(size), eFormat(format), eFlags(flags), eSwizzle(swizzle), eType(layer)
			{}
		};

		struct TLayer
		{
			float					fScale;		// world coords to norm texture coords (1/meters)
			uint					uNumBlocks;	// num block in texture side, all_blocks = sqr( num_blocks )
			uint					uFactor;	// = current level / low level world size
			///
			TLayer(): fScale(1.f), uNumBlocks(8), uFactor(2) {}
			TLayer(float scale, uint numblocks, uint factor): fScale(scale), uNumBlocks(numblocks), uFactor(factor) {}
		};


		// values
		array< TTexture >	aTextures;
		array< TLayer >		aLayers;		// for low level to high level detail
	};



	//
	// Mega Texture
	//

	class CMegaTexture : public CEngBaseObject
	{
	protected:
		struct TTexture
		{
			CTexture2DArrayPtr		pTexture;
			e_megatex_layer::type	eType;
			uint8					uTexSidePow2;	// texture side in pow of 2
			///
			TTexture():
				uTexSidePow2(0), eType(e_megatex_layer::UNKNOWN) {}
		};

		struct TLayer
		{
			ivec2					vTexCenter;		// texture center in world space (num blocks)
			vec2					vCameraPos;		// offset of camera pos to texture center (norm texc)
			float					fDist,			// distance between camera pos and nearest edge (norm texc)
									fScale,			// world coords to norm texture coords (1/m)
									fNumBlocksInv;
			irect					sCoords;		// texture coords (num blocks)
			uint8					uNumBlocks;		// num block in texture side
			uint8					uFactor;		// = current level / low level world size
			bool					bUpdating;
			///
			TLayer():
				fDist(0.f), fScale(0.f), fNumBlocksInv(0.f), uNumBlocks(1), uFactor(0), bUpdating(false) {}
		};

	protected:
		array< TTexture >	_aTextures;
		array< TLayer >		_aLayers;
		array< i8_vec2 > *	_pBlockPoints;	// left bottom points of blocks
		float				_fCorrection;	// scale correction
		uint				_uMaxLevel;

	public:
		CMegaTexture();
		~CMegaTexture();

		void Bind(uint8 uFirst) const;
		void Unbind(uint8 uFirst) const;

		bool Create(const TMTDescriptor &sDescr, array<i8_vec2> *pBuffer);
		void Destroy();

		bool UpdateAll(const ivec2 &vCenter, IMegaTextureUpdater *pUpdater);
		bool UpdateLayer(uint uLayer, uint uBaseLayer, IMegaTextureUpdater *pUpdater);

		bool FinishUpdating(const irect &sNewCoords, uint uLayer);
		bool Move(const vec2 &vOffset, IMegaTextureUpdater *pUpdater);

		bool SetMaxLevel(uint uLevel);
		
		void GetTexture(uint i, CTexture2DArrayPtr &pTex) const;
		void GetTexType(uint i, e_megatex_layer::type &eType) const;
		
		void SetCorrection(float fScale)			{ _fCorrection = fScale; }

		uint		  MaxLevel()			const	{ return _uMaxLevel; }
		uint		  NumTextures()			const	{ return (uint)_aTextures.Count(); }
		uint		  NumLayers()			const	{ return (uint)_aLayers.Count(); }
		uint		  TextureSide(uint i)	const	{ return to_bit<uint>( _aTextures[i].uTexSidePow2 ); }
		const uvec3 & TextureDim(uint i)	const	{ return _aTextures[i].pTexture->GetDimension(); }
		float		  Distance(uint i)		const	{ return _aLayers[i].fDist; }


		// CEngBaseObject //
		virtual void Release();
	};


	
/*
=================================================
	
=================================================
*/
	inline void CMegaTexture::Bind(uint8 uFirst) const
	{
		for (usize i = 0; i < _aTextures.Count(); ++i) {
			_aTextures[i].pTexture->Bind( i + uFirst );
		}
	}
	
/*
=================================================
	
=================================================
*/
	inline void CMegaTexture::Unbind(uint8 uFirst) const
	{
		for (usize i = 0; i < _aTextures.Count(); ++i) {
			_aTextures[i].pTexture->Unbind( i + uFirst );
		}
	}
	
/*
=================================================
	
=================================================
*/
	inline void CMegaTexture::GetTexture(uint i, CTexture2DArrayPtr &pTex) const
	{
		pTex = _aTextures[i].pTexture;
	}
	
/*
=================================================
	
=================================================
*/
	inline void CMegaTexture::GetTexType(uint i, e_megatex_layer::type &eType) const
	{
		eType = _aTextures[i].eType;
	}

//-------------------------------------------------------------------

}	// Resources
}	// Engine

#endif	// _UX_MEGA_TEXTURE_H